#include <jni.h>
#include <string>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <SLES/OpenSLES_AndroidConfiguration.h>
#include "RecordBuffer.h"

#ifndef FFMUSIC_ANDROIDLOG_H
#define FFMUSIC_ANDROIDLOG_H
#endif //FFMUSIC_ANDROIDLOG_H


#include "myhead.h"
#include <sys/prctl.h>


#define LOG_DEBUG true
#define LOGD(FORMAT, ...)__android_log_print(ANDROID_LOG_DEBUG,"JniThread",FORMAT, ##__VA_ARGS__);
#define LOGE(FORMAT, ...)__android_log_print(ANDROID_LOG_ERROR,"JniThread",FORMAT, ##__VA_ARGS__);
#define LOGW(FORMAT, ...)__android_log_print(ANDROID_LOG_WARN,"JniThread",FORMAT, ##__VA_ARGS__);


SL_API SLresult SLAPIENTRY slCreateEngine(
        SLObjectItf *pEngine,           //对象地址，用于传出对象
        SLuint32 numOptions,         //配置参数数量
        const SLEngineOption *pEngineOptions,    //配置参数，为枚举数组
        SLuint32 numInterfaces,      //支持的接口数量
        const SLInterfaceID *pInterfaceIds,     //具体的要支持的接口，是枚举的数组
        const SLboolean *pInterfaceRequired //具体的要支持的接口是开放的还是关闭的，也是一个数组，这三个参数长度是一致的
);

SLObjectItf slObjectEngine = NULL;//用SLObjectltf声明引擎接口对象
SLEngineItf EngineItf = NULL;//声明具体的引擎对象实例
SLObjectItf recorder = NULL;//用SLObjectltf声明引擎接口对象
SLRecordItf recordItf = NULL;
SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL;//Buffer接口
RecordBuffer *recordBuffer;


/* destination for recorded data */
FILE *pcmFile = NULL;
bool isUsePcmFile = false;
uint32_t sampleRate = 16000;
int channelCount = 6;


int bufferLength = 2400 * 6;
volatile bool finish = true;
volatile bool isrecording = false;

static JavaVM *jvm;
static jobject gInstance;
static JNIEnv *gEnv;


size_t frameSize;

/* Preset number to use for recording */
SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
SLAndroidConfigurationItf configItf;

jmethodID methodDataCallback = NULL;
jbyteArray retByteArray; //传给java层的pcm数据
jclass recordCls =NULL;


int logLevel = 2;  //日志等级
long callbackCount = 0;  // 回调次数
/**
 * 使用反射方式回调数据到java层的【dataCallback】方法
 * @param env
 *
 * buffer 是个指针数组，buffer[1]  buffer[2]
 */
void reflectDatatoJava(Byte *buffer) {
    // LOGW( "reflectDatatoJava-->start %d,", sizeof(buffer));
    if (jvm->AttachCurrentThread(&gEnv, NULL) != JNI_OK) { // Attach当前jvm线程, 构建当前线程的jvm环境
        LOGW("reflectDatatoJava--> !=JNI_OK,return");
        return;
    }
    if(recordCls ==NULL){
        recordCls = gEnv->GetObjectClass(gInstance);//得到CRecorder类
    }
    if (methodDataCallback == NULL) {
        LOGW( "reflectDatatoJava-->GetStaticMethodID");
        methodDataCallback = gEnv->GetStaticMethodID(recordCls, "dataAryCallback", "([B)V"); // byte[] 形式
    }
    if (retByteArray == NULL) {
        jbyteArray tmp = gEnv->NewByteArray(bufferLength);//分配内存
        retByteArray = static_cast<jbyteArray>(gEnv->NewGlobalRef(tmp));
        LOGW( "reflectDatatoJava-->NewByteArray");
    }
    gEnv->SetByteArrayRegion(retByteArray, 0, bufferLength,reinterpret_cast<const jbyte *>(buffer));//数据复制到jni对象,buffer数据的大小是 bufferLength
    gEnv->CallStaticVoidMethod(recordCls, methodDataCallback, retByteArray);//执行反射

}


/**
 * 缓冲数据回调
 * @param bg
 * @param context
 */
void bgRecorderCallback(SLAndroidSimpleBufferQueueItf bg, void *context) {
    prctl(PR_SET_NAME, "CRecorderCallback");//设置线程的名字
    if (NULL != recordBuffer) {
        Byte *buffer = recordBuffer->getNowBuffer();
        if(logLevel==1){
            LOGE( "传数据  reflectDatatoJava,callbackCount: %ld",callbackCount);
        }
        reflectDatatoJava(buffer);
    }
    if (finish) {
        (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);
        //释放内存---不需要
//        delete recordBuffer;
//        recordBuffer = NULL;
        LOGE("录制完成");
        isrecording = false;
    } else {
        // Enqueue 表示取数据
        if(logLevel==1){
            LOGE( "取数据  Enqueuedata,callbackCount: %ld",callbackCount);
        }
        (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recordBuffer->getRecordBuffer(),bufferLength);
        isrecording = true;

    };
    callbackCount++;
}



extern "C"
JNIEXPORT void JNICALL
Java_com_aispeech_lyra_nativec_opensles_CRecorder_initConfig(JNIEnv *env, jobject thiz,jint log_level,jint buffer_length) {

    LOGE("警告：只能初始化一次");
    LOGE("警告：只能初始化一次");
    LOGE("警告：只能初始化一次");
    LOGD("传入的配置：%d, %d",log_level,buffer_length);
    bufferLength = buffer_length;
    logLevel = log_level;  //日志等级

    frameSize = audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT) * channelCount;

    LOGW("lyra环境准备");
    (*env).GetJavaVM(&jvm);//保存全局jvm用于数据回调时的反射
    gInstance = (*env).NewGlobalRef(thiz);//转化为全局对象,这样在其它线程才能获得当前java对象的索引.
    recordBuffer = new RecordBuffer(bufferLength);
    //第一步：创建引擎
    LOGW("lyra创建引擎");


    slCreateEngine(&slObjectEngine, 0, NULL, 0, NULL, NULL);


    //第二步：实现(Realize)engineObject,SL_BOOLEAN_FALSE);实例化这个对象
    (*slObjectEngine)->Realize(slObjectEngine, SL_BOOLEAN_FALSE);
    //第三步：通过engineObject的GetInterface方法初始化enngineEngine,从这个对象里面获取引擎接口
    (*slObjectEngine)->GetInterface(slObjectEngine, SL_IID_ENGINE, &EngineItf);

    //4. 设置输出buffer队列
    SLDataLocator_AndroidSimpleBufferQueue loc_bq = {
            SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,//类型 这里只能是这个常量
            2,//buffer的数量
    };

    //5. 设置IO设备(麦克风)
    SLDataLocator_IODevice loc_dev = {
            SL_DATALOCATOR_IODEVICE,//类型
            SL_IODEVICE_AUDIOINPUT,//device类型 选择了音频输入类型
            SL_DEFAULTDEVICEID_AUDIOINPUT,//deviceID,  默认 SL_DEFAULTDEVICEID_AUDIOINPUT  ，SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
            NULL            //device实例
    };
    SLDataSource recSource = {
            &loc_dev, //SLDataLocator_IODevice配置输入
            NULL      //输入格式,采集的并不需要
    };


    //录制 16k,16bit,立体声【双路】--此配置在手机上可以成功录
    SLDataFormat_PCM format_pcm_stereo = {
            SL_DATAFORMAT_PCM,////输出PCM格式的数据
            (SLuint32) 2,//输出的声道数量
            SL_SAMPLINGRATE_16,//输出的采样频率，这里是16kHZ
            SL_PCMSAMPLEFORMAT_FIXED_16,//输出的采样格式，这里是16bit
            SL_PCMSAMPLEFORMAT_FIXED_16,//一般来说，跟随上一个参数
            SL_SPEAKER_FRONT_LEFT |
            SL_SPEAKER_FRONT_RIGHT,//双声道配置，如果单声道可以用 SL_SPEAKER_FRONT_CENTER
            SL_BYTEORDER_LITTLEENDIAN//PCM数据的大小端排列
    };


//    SLDataFormat_PCM format_pcm = {
//            SL_DATAFORMAT_PCM,//输出PCM格式的数据
//            g_num_channels,//输出的声道数量
//            g_samples_per_sec,//输出的采样频率，这里是16kHZ
//            g_bits_per_sample,//输出的采样格式，这里是16bit
//            g_bits_per_sample,//一般来说，跟随上一个参数
//            g_channel_mask,//双声道配置，如果单声道可以用 SL_SPEAKER_FRONT_CENTER
//            SL_BYTEORDER_LITTLEENDIAN//PCM数据的大小端排列
//    };

    //北汽哈曼项目
    SLDataFormat_PCM format_pcm_beiqi_2 = {
            SL_DATAFORMAT_PCM,//输出PCM格式的数据
            (SLuint32) 6,//输出的声道数量6
            SL_SAMPLINGRATE_16,//输出的采样频率，这里是16kHZ

            SL_PCMSAMPLEFORMAT_FIXED_16,//输出的采样格式，这里是16bit
            SL_PCMSAMPLEFORMAT_FIXED_16,//一般来说，跟随上一个参数

            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT
            | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY
            | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT
            | SL_ANDROID_SPEAKER_NON_POSITIONAL,   //channelmask配置

            SL_BYTEORDER_LITTLEENDIAN//PCM数据的大小端排列
    };

    SLDataSink recDest = {
            &loc_bq,     //SLDataFormat_PCM配置输出
            &format_pcm_beiqi_2  //不同的设备录制不同的数据格式的音频，改这个输出数据格式
    };


    int index = 0;
    int numInterfaces = 2;
    SLboolean required[numInterfaces];
    SLInterfaceID iidArray[numInterfaces];

    /* Initialize arrays required[] and iidArray[] */
    for (int i = 0; i < numInterfaces; i++) {
        required[i] = SL_BOOLEAN_FALSE;
        iidArray[i] = SL_IID_NULL;
    }

    required[index] = SL_BOOLEAN_TRUE;
    iidArray[index++] = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
    required[index] = SL_BOOLEAN_TRUE;
    iidArray[index++] = SL_IID_ANDROIDCONFIGURATION;

    //7. 创建录音器
    (*EngineItf)->CreateAudioRecorder(
            EngineItf,  //引擎接口
            &recorder, //录制对象地址，用于传出对象
            &recSource,  //输入配置
            &recDest,  //输出配置
            numInterfaces,            //支持的接口数量
            iidArray,          //具体的要支持的接口
            required          //具体的要支持的接口是开放的还是关闭的
    );


    LOGW("lyra GetInterface ");
    (*recorder)->GetInterface(recorder, SL_IID_ANDROIDCONFIGURATION, (void *) &configItf);
    LOGW("lyra SetConfiguration");
    (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET, &presetValue,
                                   sizeof(SLuint32));


    LOGW("lyra实例化这个录制对象 ");
    //8. 实例化这个录制对象
    (*recorder)->Realize(recorder, SL_BOOLEAN_FALSE);
    //9. 获取录制接口
    (*recorder)->GetInterface(recorder, SL_IID_RECORD, &recordItf);

    /* Set up the recorder callback to get events during the recording */
//    (*recordItf)->SetMarkerPosition(recordItf, 2000);
//    (*recordItf)->SetPositionUpdatePeriod(recordItf, 500);
//    (*recordItf)->SetCallbackEventsMask(recordItf,SL_RECORDEVENT_HEADATMARKER | SL_RECORDEVENT_HEADATNEWPOS);


    //10. 获取Buffer接口
    (*recorder)->GetInterface(recorder, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue);
    //注册音频回调接口
    (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bgRecorderCallback, NULL);
    //11. 开始录音
    LOGW("lyra 已准备好...采集回调, 每帧字节 is %d", frameSize);

}


/**
 * JNIEnv和jobject是属于线程私有的，不能共享
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_aispeech_lyra_nativec_opensles_CRecorder_startRecord(JNIEnv *env, jobject instance) {
    if (NULL != recordItf) {
        (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_RECORDING);
        (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recordBuffer->getRecordBuffer(),bufferLength);
        LOGW("startRecord  开始采集回调");
        finish = false;
    } else{
        LOGW(" 没有初始化？？？");
    }

}

extern "C"
JNIEXPORT void JNICALL
Java_com_aispeech_lyra_nativec_opensles_CRecorder_stopRecord(JNIEnv *env, jobject instance) {
    if (NULL != recordItf) {
        LOGW("stopRecord  停止采集回调");
        finish = true;
    }else{
        LOGW(" 没有初始化 ！！！");
    }
}






extern "C"
JNIEXPORT jboolean JNICALL
Java_com_aispeech_lyra_nativec_opensles_CRecorder_isInRecording(JNIEnv *env, jobject thiz) {
    LOGW("isrecording:%d", isrecording);
    if(isrecording){
        LOGW("录音中");
    } else{
        LOGW("不在录音中");
    }
    return isrecording;
}


